﻿using System;
using System.Collections;
using System.Xml;
using System.Xml.Schema;
using System.IO;
using InfoJetSoft.Service.Entity;
using InfoJetSoft.Service.Util;

namespace InfoJetSoft.Service.Worker
{
	
	class SchemaTypeWorker
	{
		
		public static SchemaType findSchemaType(Manifest manifest, string nodeName, string nsUri)
		{
			nodeName = DOMUtil.getNodeNameWithoutPrefix( nodeName );

			foreach( SchemaLink schemaLink in manifest.SchemaLinkList )
			{
				SchemaType schemaType = findSchemaTypeInSchemaLink( manifest, schemaLink, nodeName, nsUri );
				if( schemaType == null )
				{
					continue;
				}
				else
				{
					return schemaType;
				}
			}

			return SchemaType.createUnknownType();
		}

		private static SchemaType findSchemaTypeInSchemaLink( Manifest manifest, SchemaLink schemaLink, string nodeName, string nsUri)
		{
			XmlDocument schemaDoc = schemaLink.SchemaDoc;
			string targetNamespace = schemaLink.TargetNamespace;
			string xsdPrefix = getNsPrefix( schemaDoc, JetConstant.URI_XSD );
			if( targetNamespace == null || xsdPrefix == null )
			{
				return null;
			}
			//这里原来是只判断targetNamespace.Equals( nsUri ).
			//这里如果nsUri为"",那么可能是<ns:node><node1/><node2/></ns:node>中的node1,node2.
			//见InGHA837FailedClaimP.xsn.
			if( targetNamespace.Equals( nsUri ) || nsUri.Length <= 0 )
			{ 
				string filter = null;
				if( nodeName.StartsWith( "@" ) )
				{
					nodeName = nodeName.Substring( 1 );
					filter = "//" + NamespaceUtil.toFullName( xsdPrefix, "attribute" ) + "[@name='" + nodeName + "']";
				}
				else
				{
					filter = "//" + NamespaceUtil.toFullName( xsdPrefix, "element" ) + "[@name='" + nodeName + "']";
				}
				XmlElement schemaElement = (XmlElement)DOMUtil.selectANode( schemaDoc.DocumentElement, filter, schemaLink.NsResolver );
				if( schemaElement != null )
				{
					SchemaType xsdType = parseXSDType( schemaElement, xsdPrefix );
					if( xsdType != null )
					{
						//CID 0461 //2009.11.12 //Begin
						setNillable( schemaElement, xsdType );
						//CID 0461 //2009.11.12 //End
						return xsdType;
					}

					SchemaType adoType = parseADOType( schemaDoc, schemaElement );
					if( adoType != null )
					{
						//CID 0461 //2009.11.12 //Begin
						setNillable( schemaElement, adoType );
						//CID 0461 //2009.11.12 //End
						return adoType;
					}

					SchemaType simpleType = checkSimpleType( manifest, schemaLink, schemaElement );
					if( simpleType != null )
					{
						//CID 0461 //2009.11.12 //Begin
						setNillable( schemaElement, simpleType );
						//CID 0461 //2009.11.12 //End
						return simpleType;
					}

					//CID 0226 //2009.02.17 //Begin
					SchemaType complexType = checkComplexType( schemaDoc, schemaElement );
					if( complexType != null )
					{
						//CID 0461 //2009.11.12 //Begin
						setNillable( schemaElement, complexType );
						//CID 0461 //2009.11.12 //End
						return complexType;
					}
					//CID 0226 //2009.02.17 //End
					
					if( isAny( schemaElement ) )
					{
						//CID 0461 //2009.11.12 //Begin
						SchemaType anyType = SchemaType.createAnyType();
						setNillable( schemaElement, anyType );
						return anyType;
						//CID 0461 //2009.11.12 //End
					}
					
					return SchemaType.createUnknownType();
				}
			}
			return null;
		}

		//CID 0461 //2009.11.12 //Begin
		private static void setNillable( XmlElement schemaElement, SchemaType schemaType )
		{
			string nillableValue = SchemaType.false_value;
			string nillable = schemaElement.GetAttribute( "nillable" );
			if( nillable != null && nillable.ToLower().Equals( "true" ) )
			{
				nillableValue = SchemaType.true_value;
			}
			schemaType.Nillable = nillableValue;
		}
		//CID 0461 //2009.11.12 //End

		private static SchemaType parseADOType( XmlDocument schemaDoc, XmlElement schemaElement )
		{
			string qPrefix = getNsPrefix( schemaDoc, JetConstant.URI_Q );
			string dPrefix = getNsPrefix( schemaDoc, JetConstant.URI_D );
			string type = schemaElement.GetAttribute( "type" );
			if( type.Length > 0 )
			{
				SchemaType schemaType = new SchemaType();
				string[] prefixs = new string[]{ qPrefix, dPrefix };
				for( int i = 0; i < prefixs.Length; i ++ )
				{
					string prefix = prefixs[ i ];
					if( prefix != null )
					{
						if( type.Equals( NamespaceUtil.toFullName( prefix, SchemaType.optional_int ) ) )
						{
							return SchemaType.createOptionalInt();
						}
						else if( type.Equals( NamespaceUtil.toFullName( prefix, SchemaType.optional_short ) ) )
						{
							return SchemaType.createOptionalInt();
						}
						else if( type.Equals( NamespaceUtil.toFullName( prefix, SchemaType.optional_double ) ) )
						{
							return SchemaType.createOptionalDouble();
						}
						else if( type.Equals( NamespaceUtil.toFullName( prefix, SchemaType.optional_decimal ) ) )
						{
							return SchemaType.createOptionalDouble();
						}
						else if( type.Equals( NamespaceUtil.toFullName( prefix, SchemaType.optional_Boolean ) ) )
						{
							return SchemaType.createOptionalBoolean();
						}
						else if( type.Equals( NamespaceUtil.toFullName( prefix, SchemaType.optional_dateTime_notz ) ) )
						{
							return SchemaType.createOptionalDateTimeNotz();
						}
						else if( type.Equals( NamespaceUtil.toFullName( prefix, SchemaType.dateTime_notz ) ) )
						{
							return SchemaType.createDateTimeNotz();
						}
					}
				}
			}
			return null;
		}

		private static SchemaType parseXSDType( XmlElement schemaElement, string xsdPrefix )
		{
			string type = schemaElement.GetAttribute( "type" );
			if( type.Length > 0 )
			{
				SchemaType schemaType = new SchemaType();
							
				if( type.Equals( NamespaceUtil.toFullName( xsdPrefix, SchemaType._integer ) ) )
				{
					schemaType.Type = SchemaType._integer;
				}
				else if( type.Equals( NamespaceUtil.toFullName( xsdPrefix, SchemaType._int ) ) )
				{
					schemaType.Type = SchemaType._integer;
				}
				else if( type.Equals( NamespaceUtil.toFullName( xsdPrefix, SchemaType._double ) ) )
				{
					schemaType.Type = SchemaType._double;
				}
				//CID 0155 //2008.11.20 //Begin
				else if( type.Equals( NamespaceUtil.toFullName( xsdPrefix, SchemaType._decimal ) ) )
				{
					schemaType.Type = SchemaType._double;
				}
				else if( type.Equals( NamespaceUtil.toFullName( xsdPrefix, SchemaType._float ) ) )
				{
					schemaType.Type = SchemaType._double;
				}
				//CID 0155 //2008.11.20 //End
				else if( type.Equals( NamespaceUtil.toFullName( xsdPrefix, SchemaType._boolean ) ) )
				{
					schemaType.Type = SchemaType._boolean;
				}
				else if( type.Equals( NamespaceUtil.toFullName( xsdPrefix, SchemaType._anyURI ) ) )
				{
					schemaType.Type = SchemaType._anyURI;
				}
				else if( type.Equals( NamespaceUtil.toFullName( xsdPrefix, SchemaType._date ) ) )
				{
					schemaType.Type = SchemaType._date;
				}
				else if( type.Equals( NamespaceUtil.toFullName( xsdPrefix, SchemaType._time ) ) )
				{
					schemaType.Type = SchemaType._time;
				}
				else if( type.Equals( NamespaceUtil.toFullName( xsdPrefix, SchemaType._dateTime ) ) )
				{
					schemaType.Type = SchemaType._dateTime;
				}
				else if( type.Equals( NamespaceUtil.toFullName( xsdPrefix, SchemaType._string ) ) )
				{
					schemaType.Type = SchemaType._string;
				}
				else if( type.Equals( NamespaceUtil.toFullName( xsdPrefix, SchemaType._base64Binary ) ) )
				{
					schemaType.Type = SchemaType._base64Binary;
				}
				else
				{
					return null;
				}
				return schemaType;
			}
			return null;
		}

		//CID 0226 //2009.02.17 //Begin
		public static SchemaType checkComplexType( XmlDocument schemaDoc, XmlElement schemaElement )
		{
			XmlElement complexTypeElement = DOMUtil.findChildElementByLocalName( schemaElement, "complexType" );
			if( complexTypeElement != null )
			{
				XmlElement simpleContentElement = DOMUtil.findChildElementByLocalName( complexTypeElement, "simpleContent" );
				if( simpleContentElement != null )
				{
					XmlElement extensionElement = DOMUtil.findChildElementByLocalName( simpleContentElement, "extension" );
					if( extensionElement != null )
					{
						string baseType = extensionElement.GetAttribute( "base" );
						//CID 0502 //2010.01.22 //Begin
						foreach( string simpleType in SchemaType.SimpleTypes )
						{
							//这里可以输出任何类型,Browser只会判断需要判断的类型.
							//这里输出的类型在大多数情况下和Formatting没有关系,除了对于int(integer),需要转换成整数.
							if( baseType.Equals( NamespaceUtil.toFullName( extensionElement.Prefix, simpleType ) ) )
							{
								SchemaType schemaType = new SchemaType();
								schemaType.Type	= simpleType;
								setNillable( schemaElement, schemaType );
								return schemaType;
							}
						}
						//CID 0502 //2010.01.22 //End
					}
				}
			}
			return null;
		}
		//CID 0226 //2009.02.17 //End

		private static SchemaType checkSimpleType( Manifest manifest, SchemaLink schemaLink, XmlElement schemaElement )
		{
			string type = schemaElement.GetAttribute( "type" );
			if( type == null || type.Length <= 0 )
			{
				SchemaType innerSimpleType = checkInnerSimpleType( manifest, schemaLink, schemaElement );
				return innerSimpleType;
			}
			string typeName = NamespaceUtil.getLocalName( type );
			XmlElement root = schemaLink.SchemaDoc.DocumentElement;
			XmlElement simpleTypeElement = DOMUtil.findChildElementByLocalNameAndAttr( root, "simpleType", "name", typeName );
			if( simpleTypeElement == null )
			{
				return null;
			}
			return parseSimpleType( manifest, schemaLink, schemaElement, simpleTypeElement );
		}

		private static SchemaType checkInnerSimpleType( Manifest manifest, SchemaLink schemaLink, XmlElement schemaElement )
		{
			XmlElement simpleTypeElement = DOMUtil.findChildElementByLocalName( schemaElement, "simpleType" );
			if( simpleTypeElement == null )
			{
				return null;
			}
			return parseSimpleType( manifest, schemaLink, schemaElement, simpleTypeElement );
		}

		//解析simpleType类型,simpleType主要是其中有许多restriction,现在没有支持所有的restriction,如pattern.
		//在infojet.js中,对string anyURI的支持是不完全的(没有检查maxLength),对integer则没有检查inclusive.
		private static SchemaType parseSimpleType( Manifest manifest, SchemaLink schemaLink, XmlElement schemaElement, XmlElement simpleTypeElement )
		{
			XmlElement restrictionElement = DOMUtil.findChildElementByLocalName( simpleTypeElement, "restriction" );
			if( restrictionElement == null )
			{
				return null;
			}
			string typeBase = restrictionElement.GetAttribute( "base" );
			if( typeBase == null || typeBase.Length <= 0 )
			{
				return null;
			}
			//CID 0429 //2009.10.18 //Begin
			string typeBaseName = parseTypeBaseName( manifest, schemaLink, typeBase );
			//CID 0429 //2009.10.18 //End
			if( typeBaseName.Equals( "string" ) || typeBaseName.Equals( "anyURI" ) )
			{
				string minLengthValue = null;
				XmlElement minLengthElement = DOMUtil.findChildElementByLocalName( restrictionElement, "minLength" );
				if( minLengthElement != null )
				{
					minLengthValue = minLengthElement.GetAttribute( "value" );
				}
				string maxLengthValue = null;
				XmlElement maxLengthElement = DOMUtil.findChildElementByLocalName( restrictionElement, "maxLength" );
				if( maxLengthElement != null )
				{
					maxLengthValue = maxLengthElement.GetAttribute( "value" );
				}
				SchemaType schemaType = new SchemaType();
				schemaType.Type = typeBaseName;
				schemaType.MinLength = minLengthValue;
				schemaType.MaxLength = maxLengthValue;
				return schemaType;
			}
			//integer类型的约束,例子见CSPF.xsn
			//decimal类型约束,见test1.xsn
			//CID 0429 //2009.10.18 //decimal
			if( typeBaseName.Equals( "integer" ) || typeBaseName.Equals( "decimal" ) )
			{
				string minInclusiveValue = null;
				XmlElement minInclusiveElement = DOMUtil.findChildElementByLocalName( restrictionElement, "minInclusive" );
				if( minInclusiveElement != null )
				{
					minInclusiveValue = minInclusiveElement.GetAttribute( "value" );
				}
				string maxInclusiveValue = null;
				XmlElement maxInclusiveElement = DOMUtil.findChildElementByLocalName( restrictionElement, "maxInclusive" );
				if( maxInclusiveElement != null )
				{
					maxInclusiveValue = maxInclusiveElement.GetAttribute( "value" );
				}
				SchemaType schemaType = new SchemaType();
				schemaType.Type = typeBaseName;
				schemaType.MinInclusive = minInclusiveValue;
				schemaType.MaxInclusive = maxInclusiveValue;
				return schemaType;
			}
			//base64Binary,例子见Support\Piotr\2008.05.07\reattachementsnextissue\2.xsn
			if( typeBaseName.Equals( "base64Binary" ) )
			{
				SchemaType schemaType = new SchemaType();
				schemaType.Type = SchemaType._base64Binary;
				return schemaType;
			}
			return null;
		}

		//CID 0429 //2009.10.18 //Begin
		private static string parseTypeBaseName( Manifest manifest, SchemaLink schemaLink, string typeBase )
		{
			if( isBuiltInType( schemaLink, typeBase ) )
			{
				return NamespaceUtil.getLocalName(typeBase);
			}
			else
			{
				string nsUri = getTypeNsUri( schemaLink, typeBase );
				if( nsUri != null )
				{
					string typeName = NamespaceUtil.getLocalName(typeBase);
					foreach( SchemaLink otherSchemaLink in manifest.SchemaLinkList )
					{
						if( otherSchemaLink.TargetNamespace != null && otherSchemaLink.TargetNamespace.Equals( nsUri ) )
						{
							XmlElement root = otherSchemaLink.SchemaDoc.DocumentElement;
							XmlElement simpleTypeElement = DOMUtil.findChildElementByLocalNameAndAttr(root, "simpleType", "name", typeName);
							if (simpleTypeElement != null)
							{
								XmlElement restrictionElement = DOMUtil.findChildElementByLocalName(simpleTypeElement, "restriction");
								if (restrictionElement != null)
								{
									string innerTypeBase = restrictionElement.GetAttribute("base");
									if ( isBuiltInType( otherSchemaLink, innerTypeBase ) )
									{
										return NamespaceUtil.getLocalName(innerTypeBase);
									}
								}
							}
						}
					}
				}
			}
			return typeBase;
		}
		//CID 0429 //2009.10.18 //End

		//CID 0429 //2009.10.18 //Begin
		private static bool isBuiltInType( SchemaLink schemaLink, string typeBase )
		{
			string nsUri = getTypeNsUri( schemaLink, typeBase );
			if( nsUri != null && nsUri.Equals( JetConstant.URI_XSD ) )
			{
				return true;
			}
			return false;
		}
		//CID 0429 //2009.10.18 //End

		//CID 0429 //2009.10.18 //Begin
		private static string getTypeNsUri( SchemaLink schemaLink, string typeBase )
		{
			if( typeBase != null )
			{
				string nsPrefix = NamespaceUtil.getPrefix( typeBase );
				if( nsPrefix != null )
				{
					string nsUri = schemaLink.NsResolver.GetURI( nsPrefix );
					return nsUri;
				}
			}
			return null;
		}
		//CID 0429 //2009.10.18 //End

		private static bool isAny( XmlElement schemaElement )
		{
			XmlElement complexTypeElement = DOMUtil.findChildElementByLocalName( schemaElement, "complexType" );
			if( complexTypeElement != null )
			{
				XmlElement sequenceElement = DOMUtil.findChildElementByLocalName( complexTypeElement, "sequence" );
				if( sequenceElement != null )
				{
					XmlElement anyElement = DOMUtil.findChildElementByLocalName( sequenceElement, "any" );
					if( anyElement != null )
					{
						return true;
					}
				}
			}
			return false;
		}

		private static string getNsPrefix( XmlDocument schemaDoc, string uri )
		{
			foreach( XmlAttribute attr in schemaDoc.DocumentElement.Attributes )
			{
				if( attr.Value.Equals( uri ) )
				{
					if( attr.Prefix != null && attr.Prefix.Equals( "xmlns" ) )
					{
						return attr.LocalName;
					}
					if( attr.Name.Equals( "xmlns" ) )
					{
						return "";
					}
				}
			}
			return null;
		}

		//CID 0331 //2009.07.27 //Begin
		public static void buildXhtmlFieldNameList( Manifest manifest )
		{
			manifest.XhtmlElementNameList = new ArrayList();
			XmlDocument schemaDoc = manifest.RootSchemaDef.SchemaLink.SchemaDoc;
			SimpleNamespaceResolver nsResolver = new SimpleNamespaceResolver( schemaDoc.NameTable );
			nsResolver.AddNamespace( "xsd", "http://www.w3.org/2001/XMLSchema" );
			XmlNodeList anyElementList = DOMUtil.selectNodes( schemaDoc, "/xsd:schema/xsd:element/xsd:complexType/xsd:sequence/xsd:any", 0, nsResolver );
			foreach( XmlElement anyElement in anyElementList )
			{
				string ns = anyElement.GetAttribute( "namespace" );
				if( ns.Equals( "http://www.w3.org/1999/xhtml" ) )
				{
					XmlElement elementElement = anyElement.ParentNode.ParentNode.ParentNode as XmlElement;
					string name = elementElement.GetAttribute( "name" );
					if( name != null && name.Length > 0 )
					{
						manifest.XhtmlElementNameList.Add( name );
					}
				}
			}
		}
		//CID 0331 //2009.07.27 //End

		//CID 0331 //2009.07.27 //Begin
		public static bool isXhtmlElementByLocalName( Manifest manifest, string elementLocalName )
		{
			foreach( string xhtmlElementName in manifest.XhtmlElementNameList )
			{
				if( xhtmlElementName.Equals( elementLocalName ) )
				{
					return true;
				}
			}
			return false;
		}
		//CID 0331 //2009.07.27 //End

		//CID 0331 //2009.07.27 //Begin
		public static bool isXhtmlElementByFullName( Manifest manifest, string elementFullName )
		{
			string elementLocalName = elementFullName;
			int commaIndex = elementFullName.IndexOf( ":" );
			if( commaIndex > 0 )
			{
				elementLocalName = elementFullName.Substring( commaIndex + 1 );
			}
			return isXhtmlElementByLocalName( manifest, elementLocalName );
		}
		//CID 0331 //2009.07.27 //End

	}
}